package com.net.xpay.admin.controller.admin;

import com.alipay.api.AlipayApiException;
import com.alipay.api.response.AlipayFundAccountbookNotifySubscribeResponse;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.net.common.exception.BusinessException;
import com.net.common.model.ResultData;
import com.net.common.model.ResultPagedList;
import com.net.common.util.LocalDateUtil;
import com.net.common.util.MoneyUtil;
import com.net.common.util.PhoneUtil;
import com.net.common.util.StreamUtil;
import com.net.common.web.CookieHelper;
import com.net.xpay.admin.po.UpdateUserPo;
import com.net.xpay.admin.secutiry.CurrentOperator;
import com.net.xpay.admin.secutiry.SessionManager;
import com.net.xpay.admin.vo.AppKeyVo;
import com.net.xpay.admin.vo.UserExportVo;
import com.net.xpay.admin.vo.UserRegisterVo;
import com.net.xpay.admin.vo.UserVo;
import com.net.xpay.alipay.safePay.PoseidonSafePayAlipayClient;
import com.net.xpay.common.annotation.OperateLog;
import com.net.xpay.common.domain.CheckCode;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.domain.UserShard;
import com.net.xpay.common.domain.admin.rbac.AdminUser;
import com.net.xpay.common.domain.operator.OperatorUser;
import com.net.xpay.common.domain.operator.OperatorUserBinding;
import com.net.xpay.common.domain.partner.Partner;
import com.net.xpay.common.domain.partner.PartnerCode;
import com.net.xpay.common.enums.config.CommonSysConfigKey;
import com.net.xpay.common.enums.config.FunctionModule;
import com.net.xpay.common.enums.config.MethodType;
import com.net.xpay.common.enums.user.CheckCodeType;
import com.net.xpay.common.enums.user.ClientFrom;
import com.net.xpay.common.enums.user.SecurityStatus;
import com.net.xpay.common.enums.user.UserTag;
import com.net.xpay.common.helper.CommonSysConfigHelper;
import com.net.xpay.common.manager.CheckCodeManager;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.common.manager.UserShardManager;
import com.net.xpay.common.manager.operator.OperatorUserBindingManager;
import com.net.xpay.common.manager.operator.OperatorUserManager;
import com.net.xpay.common.manager.partner.PartnerCodeManager;
import com.net.xpay.common.manager.partner.PartnerManager;
import com.net.xpay.common.model.RegisterModel;
import com.net.xpay.common.model.UpdateUserServicePriceModel;
import com.net.xpay.common.model.search.UserSearchModel;
import com.net.xpay.common.service.SafePayBookPreApplyService;
import com.net.xpay.common.service.UserService;
import com.net.xpay.common.validator.CheckCodeValidator;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.WebConstant;
import com.net.xpay.core.domain.UserAccount;
import com.net.xpay.core.domain.safePay.SafePayBookAccount;
import com.net.xpay.core.enums.AlipayUserStatus;
import com.net.xpay.core.enums.SafePayBookAccountSignStatus;
import com.net.xpay.core.helper.DomainHelper;
import com.net.xpay.core.manager.AlipayUserManager;
import com.net.xpay.core.manager.UserAccountManager;
import com.net.xpay.core.manager.safePay.SafePayBookAccountManager;
import com.net.xpay.core.model.AlipayUserSearchModel;
import com.net.xpay.core.model.IdModel;
import com.net.xpay.core.model.SafePayBookAccountSearchModel;
import com.net.xpay.core.model.UpdatePartnerModel;
import com.net.xpay.core.utils.BeanCopierUtil;
import com.net.xpay.core.utils.JxlsUtils;
import com.net.xpay.core.utils.UuidUtil;
import com.net.shard.ShardUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 2019/4/1 14:45
 */
@Slf4j
@RestController
@RequestMapping("/admin/user")
@Api(tags = "运营/商户管理", description = "运营 商户管理 net")
public class UserController {

    @Autowired
    private UserManager userManager;

    @Autowired
    private UserService userService;

    @Autowired
    private UserAccountManager userAccountManager;

    @Autowired
    private UserShardManager userShardManager;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private PartnerCodeManager partnerCodeManager;

    @Autowired
    private CommonSysConfigHelper commonSysConfigHelper;

    @Autowired
    private DomainHelper domainHelper;

    @Autowired
    private SessionManager sessionManager;

    @Autowired
    private CheckCodeManager checkCodeManager;

    @Autowired
    private CheckCodeValidator checkCodeValidator;

    @Autowired
    private SafePayBookAccountManager safePayBookAccountManager;

    @Autowired
    private AlipayUserManager alipayUserManager;

    @Autowired
    private PoseidonSafePayAlipayClient xpaySafePayAlipayClient;

    @Autowired
    private PartnerManager partnerManager;

    @Autowired
    private OperatorUserBindingManager operatorUserBindingManager;

    @Autowired
    private OperatorUserManager operatorUserManager;

    @Autowired
    private SafePayBookPreApplyService safePayBookPreApplyService;

    @ApiOperation("搜索")
    @PostMapping("/search")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::search')")
    public ResultPagedList<UserVo> searchMerchant(@ApiIgnore @CurrentOperator AdminUser adminUser, @RequestBody UserSearchModel searchModel) {
        if (!Strings.isNullOrEmpty(searchModel.getOperatorUserMobile())) {
            OperatorUser operatorUser = operatorUserManager.findByMobile(searchModel.getOperatorUserMobile());
            if (operatorUser != null) {
                List<OperatorUserBinding> operatorUserBindingList = operatorUserBindingManager.getByOperatorIdList(operatorUser.getId());
                ArrayList<Long> userIds = new ArrayList<>();
                for (OperatorUserBinding operatorUserBinding : operatorUserBindingList) {
                    if (operatorUserBinding != null) {
                        userIds.add(operatorUserBinding.getUserId());
                    }
                }
                searchModel.setIds(userIds);
            } else {
                return new ResultPagedList<>(new ArrayList<>(), 0L, searchModel);
            }
        }

        List<User> userList = userManager.search(searchModel);
        long count = userManager.searchCount(searchModel);

        List<UserVo> userVoList = BeanCopierUtil.batchCopy(User.class, UserVo.class, userList);
        if (CollectionUtils.isNotEmpty(userList)) {
            List<Long> userIdList = StreamUtil.map(userList, User::getId);
            List<UserShard> userShards = userShardManager.findByIds(userIdList);
            Map<Long, UserShard> userShardMap = StreamUtil.toMap(userShards, UserShard::getId);
            Map<Long, User> userMap = StreamUtil.toMap(userList, User::getId);
            for (UserVo vo : userVoList) {
                try {
                    UserShard userShard = userShardMap.get(vo.getId());
                    if (userShard != null) {
                        ShardUtil.setShardContext(userShardMap.get(vo.getId()).getShardId());
                    }
                    UserAccount userAccount = userAccountManager.getById(vo.getId());
                    if(userAccount!=null){
                        vo.setMoneyLeft(userAccount.getMoneyLeft());
                        vo.setTransBatchLeft(userAccount.getTransBatchLeft());
                    }
                    UserProfile userProfile = userProfileManager.getById(vo.getId());
                    //安全发签约数
                    SafePayBookAccountSearchModel safePayBookAccountSearchModel = new SafePayBookAccountSearchModel();
                    safePayBookAccountSearchModel.setUserId(vo.getId());
                    safePayBookAccountSearchModel.setSignStatus(SafePayBookAccountSignStatus.NORMAL);
                    Long safePaySignCount = safePayBookAccountManager.searchCount(safePayBookAccountSearchModel);
                    // 记账本ID
                    String bookIds = "";
                    List<SafePayBookAccount> bookAccountList = safePayBookAccountManager.search(safePayBookAccountSearchModel);
                    for (int i = 0; i < bookAccountList.size(); i++) {
                        if (i == bookAccountList.size() - 1) {
                            bookIds += bookAccountList.get(i).getBookId();
                        } else {
                            bookIds += bookAccountList.get(i).getBookId() + ",";
                        }
                    }
                    vo.setBookIds(bookIds);
                    //批量有密签约数
                    AlipayUserSearchModel alipayUserSearchModel = new AlipayUserSearchModel();
                    alipayUserSearchModel.setUserId(vo.getId());
                    alipayUserSearchModel.setStatus(AlipayUserStatus.YES);
                    alipayUserSearchModel.setIsDeleted(0);
                    Long payTransSignCount = alipayUserManager.searchCount(alipayUserSearchModel);

                    vo.setPayTransSignCount(payTransSignCount);
                    vo.setSafePaySignCount(safePaySignCount);

                    if (userProfile == null) {
                        userProfile = userService.createUserProfile(vo.getId());
                    }
                    vo.setUserProfile(userProfile);


                    vo.setUserTagName(vo.getUserTag().getDesc());
                    if (vo.getIndustryCategory() == null) {
                        vo.setIndustryCategoryName("");
                    } else {
                        vo.setIndustryCategoryName(vo.getIndustryCategory().getDesc());
                    }

                    vo.setAppVersionName(vo.getAppVersion().getDesc());

                    if (!Strings.isNullOrEmpty(vo.getPma())) {
                        PartnerCode partnerCode = partnerCodeManager.findByPma(vo.getPma());
                        vo.setPartnerCode(partnerCode);
                    }

                    User user = userMap.get(vo.getId());
                    vo.setHasAppkey(!Strings.isNullOrEmpty(user.getAppkey()));

                    if (vo.getCostServiceRatio() != null) {
                        vo.setPartnerServiceRatio(vo.getServiceRatio().subtract(vo.getCostServiceRatio()));
                    }
                    if (vo.getBankCostServiceRatio() != null) {
                        vo.setPartnerBankServiceRatio(vo.getBankServiceRatio().subtract(vo.getBankCostServiceRatio()));
                    }
                    OperatorUserBinding operatorUserBinding = operatorUserBindingManager.findByUserId(vo.getId());
                    if (operatorUserBinding != null) {
                        OperatorUser operatorUser = operatorUserManager.getById(operatorUserBinding.getOperatorUserId());
                        if (operatorUser != null) {
                            vo.setOperatorUserAccount(operatorUser.getMobile());
                        }
                    }
                } catch (Exception e) {
                    log.warn("遍历商户信息，id=" + vo.getId(), e);
                } finally {
                    ShardUtil.cleanShardContext();
                }
            }
        }

        return new ResultPagedList<>(userVoList, count, searchModel);
    }

    @ApiOperation("获取单个用户")
    @GetMapping
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::get')")
    public ResultData<UserVo> searchMerchantDetails(Long id) {
        Preconditions.checkArgument(id != null);
        User user = userManager.getById(id);
        UserVo vo = new UserVo();
        BeanCopierUtil.copy(user, vo);
        UserShard userShard = userShardManager.getById(id);
        try {
            ShardUtil.setShardContext(userShard.getShardId());
            UserAccount userAccount = userAccountManager.getById(vo.getId());
            vo.setMoneyLeft(userAccount.getMoneyLeft());
            vo.setTransBatchLeft(userAccount.getTransBatchLeft());
            UserProfile userProfile = userProfileManager.getById(vo.getId());
            if (userProfile == null) {
                userProfile = userService.createUserProfile(vo.getId());
            }
            vo.setUserProfile(userProfile);

            vo.setUserTagName(vo.getUserTag().getDesc());
            if (vo.getIndustryCategory() == null) {
                vo.setIndustryCategoryName("");
            } else {
                vo.setIndustryCategoryName(vo.getIndustryCategory().getDesc());
            }
            vo.setAppVersionName(vo.getAppVersion().getDesc());
        } finally {
            ShardUtil.cleanShardContext();
        }

        return new ResultData<>(vo);
    }

    @ApiOperation("注册")
    @PostMapping("/register")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::register')")
    @OperateLog(methodType = MethodType.INSERT, methodDesc = "注册用户", module = FunctionModule.MERCHANT_REGISTER)
    public ResultData<UserRegisterVo> register(@RequestBody RegisterModel registerModel) {
        //验证手机号
        if (!PhoneUtil.isEffectiveMobile(registerModel.getMobile())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "手机号格式不正确");
        }
        //确认手机号未被注册
        if (userManager.findByMobile(registerModel.getMobile()) != null) {
            throw new BusinessException(PoseidonErrorCode.MOBILE_DUPLICATED, "手机号已经被注册,你可以使用找回密码功能");
        }

        String randomPassword = Strings.padStart("" + RandomUtils.nextInt(0, 1000000), 6, '0');
        registerModel.setPassword(randomPassword);
        registerModel.setClientFrom(ClientFrom.PC_WEB);
        registerModel.setAdminRegister(true);
        registerModel.setUserTag(UserTag.PLATFORM);
        User user = userService.registerInternal(registerModel);

        String website = commonSysConfigHelper.getString(CommonSysConfigKey.MERCHANT_DOMAIN);
        String openDomain = commonSysConfigHelper.getString(CommonSysConfigKey.OPEN_DOMAIN);
        String openapiDomain = commonSysConfigHelper.getString(CommonSysConfigKey.OPENAPI_DOMAIN);
        UserRegisterVo vo = new UserRegisterVo();
        vo.setMobile(user.getMobile());
        vo.setPassword(randomPassword);
        vo.setWebsite(website);
        vo.setOpenDomain(openDomain);
        vo.setOpenapiDomain(openapiDomain);
        return new ResultData<>(vo);
    }

    @ApiOperation("设置手续费")
    @PostMapping("/update-service-unit-price")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-service-unit-price')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "设置手续费", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<User> updateServiceUnitPrice(@RequestBody UpdateUserServicePriceModel model) {
        User user = userService.updateServicePrice(model);
        return new ResultData<>(user);
    }

    @ApiOperation("修改商户信息")
    @PostMapping("/update-user-info")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-user-info')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "修改商户信息", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<User> updateUserInfo(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null);
        User user = userManager.getById(po.getId());
        user.setName(po.getName());
        user.setRemark(po.getRemark());
        user.setIndustryCategory(po.getIndustryCategory());
        userManager.updateByPrimaryKeyWithVersion(user);

        UserProfile userProfile = userProfileManager.getById(user.getId());
        userProfile.setTransAmountDayLimit(po.getTransAmountDayLimit());
        userProfile.setSafePayTransAmountDayLimit(po.getSafePayTransAmountDayLimit());

        if (!Strings.isNullOrEmpty(po.getReceiveMobile())) {
            userProfile.setReceiveMobile(po.getReceiveMobile().trim());
        }
        if (!Strings.isNullOrEmpty(po.getSpareReceiveMobile())) {
            userProfile.setSpareReceiveMobile(po.getSpareReceiveMobile().trim());
        }
        if (po.getBatchEnsure() != null) {
            userProfile.setBatchEnsure(po.getBatchEnsure());
        }
        if (po.getEnsureBatchAmount() != null) {
            userProfile.setEnsureBatchAmount(po.getEnsureBatchAmount());
        }
        if (po.getStrictLogin() != null) {
            userProfile.setStrictLogin(po.getStrictLogin());
        }
        if (po.getUseGoogleAuth() != null) {
            userProfile.setUseGoogleAuth(po.getUseGoogleAuth());
        }
        if (po.getNeedSafePayAuth() != null) {
            userProfile.setNeedSafePayAuth(po.getNeedSafePayAuth());
        }
        if (po.getLimitSafePayWebPageTrans() != null) {
            userProfile.setLimitSafePayWebPageTrans(po.getLimitSafePayWebPageTrans());
        }

        if (po.getNoWechatQrCode() != null) {
            userProfile.setNoWechatQrCode(po.getNoWechatQrCode());
        }
        if (po.getOpenMerchantPlatform() != null) {
            userProfile.setOpenMerchantPlatform(po.getOpenMerchantPlatform());
        }
        userProfileManager.update(userProfile);

        return new ResultData<>(user);
    }

    @ApiOperation("修改手机号")
    @PostMapping("/update-mobile")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-mobile')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "修改商户手机号", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<User> updateMobile(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && !Strings.isNullOrEmpty(po.getMobile()));
        if (Strings.isNullOrEmpty(po.getCheckCode())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "验证码不能为空");
        }
        User preUser = userManager.findByMobile(po.getMobile());
        if (preUser != null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "手机号已被注册");
        }

        User user = userManager.getById(po.getId());
        CheckCode checkCode = checkCodeManager.findByMobileAndType(user.getMobile(), CheckCodeType.MODIFY_MOBILE);
        checkCodeValidator.validateCheckCode(checkCode, po.getCheckCode());
        user.setMobile(po.getMobile());
        userManager.updateByPrimaryKeyWithVersion(user);
        UserShard userShard = userShardManager.getById(user.getId());
        userShard.setMobile(po.getMobile());
        userShardManager.updateByPrimaryKeyWithVersion(userShard);

        return new ResultData<>(user);
    }

    @ApiOperation("修改用户状态")
    @PostMapping("/update-user-security-status")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-user-security-status')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "修改商户状态", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<User> updateUserSecurityStatus(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null);
        User user = userManager.getById(po.getId());
        user.setSecurityStatus(po.getSecurityStatus());
        user.setSecurityRemark(po.getSecurityRemark());
        userManager.updateByPrimaryKeyWithVersion(user);
        return new ResultData<>(user);
    }

    @ApiOperation("设置推送地址")
    @PutMapping("/update-user-charge-notify-url")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-user-charge-notify-url')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "设置推送地址", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateChargeNotifyUrl(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenChargeNotify() != null);

        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenChargeNotify(po.getOpenChargeNotify());
        userProfile.setChargeNotifyUrl(po.getChargeNotifyUrl());
        userProfile.setOpenSafePayNotify(po.getOpenSafePayNotify());
        userProfile.setSafePayNotifyUrl(po.getSafePayNotifyUrl());

        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("生成接口秘钥")
    @PostMapping("/create-appkey")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::create-appkey')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "生成商户接口秘钥", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<User> createAppkey(Long userId) {
        Preconditions.checkArgument(userId != null);

        User user = userManager.getById(userId);

        if (!Strings.isNullOrEmpty(user.getAppkey())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "用户秘钥已生成，请勿重复生成！");
        }
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (!Boolean.TRUE.equals(userProfile.getOpenMerchantPlatform())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "未开放商户端，不需要生成秘钥，请选择创建应用");
        }

        user.setAppkey(UuidUtil.uuid());
        user.setSecret(UuidUtil.uuid());

        userProfile.setBatchEnsure(false);
        userProfile.setStrictLogin(true);
        userProfileManager.update(userProfile);
        userManager.updateByPrimaryKeyWithVersion(user);
        return new ResultData<>(user);
    }

    @ApiOperation("重置商户密码")
    @PostMapping("/reset-password")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::reset-password')")
    public ResultData<String> resetPassword(@RequestBody IdModel model) {
        Preconditions.checkArgument(model.getId() != null);
        User user = userManager.getById(model.getId());
        String psw = Strings.padStart("" + RandomUtils.nextInt(0, 1000000), 6, '0');
        user.setPassword(BCrypt.hashpw(psw, BCrypt.gensalt()));
        userManager.updateByPrimaryKeyWithVersion(user);
        return new ResultData<>(psw);
    }

    @ApiOperation("查看秘钥")
    @PostMapping("/select-appkey")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::select-appkey')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "查看秘钥", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<AppKeyVo> selectAppkey(@RequestBody IdModel model) {
        Preconditions.checkArgument(model.getId() != null);

        User user = userManager.getById(model.getId());
        if (Strings.isNullOrEmpty(user.getAppkey())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "用户秘钥暂未生成，请先去生成接口秘钥！");
        }
        AppKeyVo vo = new AppKeyVo();
        vo.setAppkey(user.getAppkey());
        vo.setSecret(user.getSecret());
        return new ResultData<>(vo);
    }

    @ApiOperation("合伙人换绑")
    @PostMapping("/update-partner")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-partner')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "商户合伙人换绑", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<Boolean> updatePartner(@RequestBody UpdatePartnerModel model) {
        Preconditions.checkArgument(model.getId() != null || model.getPartnerId() != null);
        userService.updatePartner(model);
        return new ResultData<>(true);
    }

    @ApiOperation("开启关闭充值页面")
    @PostMapping("/update-open-charge")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-charge')")
    public ResultData<UserProfile> updateOpenCharge(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenCharge() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenCharge(po.getOpenCharge());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启关闭安全付接口权限")
    @PostMapping("/update-open-safe-pay")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-safe-pay')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "开启关闭安全付接口权限", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateOpenSafePay(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenSafePay() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenSafePay(po.getOpenSafePay());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启关闭安全付汇款充值")
    @PostMapping("/update-open-safe-pay-offline-charge")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "开启关闭安全付汇款充值", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateOpenSafePayOfflineCharge(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenSafePayOfflineCharge() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenSafePayOfflineCharge(po.getOpenSafePayOfflineCharge());
        log.info("start updateOpenSafePayOfflineCharge");
        //订阅一下入金消息
        if (Boolean.TRUE.equals(po.getOpenSafePayOfflineCharge())) {
            SafePayBookAccountSearchModel searchModel = new SafePayBookAccountSearchModel();
            searchModel.setUserId(userProfile.getId());
            searchModel.setSignStatus(SafePayBookAccountSignStatus.NORMAL);
            UserShard userShard = userShardManager.getById(userProfile.getId());
            try {
                ShardUtil.setShardContext(userShard.getShardId());
                List<SafePayBookAccount> accounts = safePayBookAccountManager.list(searchModel);
                log.info("updateOpenSafePayOfflineCharge accounts size={}", accounts.size());
                for (SafePayBookAccount account : accounts) {
                    try {
                        log.info("updateOpenSafePayOfflineCharge account = {}", account.getAlipayNickName());
                        AlipayFundAccountbookNotifySubscribeResponse subscribeResponse = xpaySafePayAlipayClient.alipayFundAccountbookNotifySubscribe(account.getBookId(), account.getAgreementNo());
                        if (subscribeResponse.isSuccess() && "VALID".equals(subscribeResponse.getSubscribeStatus())) {
                            log.info("saveSafePayBookAccount subscribe success|account={}", account.getAlipayNickName());
                        } else {
                            log.info("saveSafePayBookAccount subscribe failed|account={}", account.getAlipayNickName());
                        }
                    } catch (AlipayApiException e) {
                        throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "开启失败:消息订阅错误:" + account.getAlipayNickName());
                    }
                }
            } finally {
                ShardUtil.cleanShardContext();
            }

        }

        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }


    @ApiOperation("开启关闭自动付接口权限")
    @PostMapping("/update-open-auto-pay")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-auto-pay')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "开启关闭自动付接口权限", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateOpenAutoPay(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenAutoPay() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenAutoPay(po.getOpenAutoPay());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启关闭安全付收集到卡权限")
    @PostMapping("/update-open-collection-safe-pay")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-collection-safe-pay')")
    public ResultData<UserProfile> updateOpenCollectionSafePay(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenCollectionSafePay() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenCollectionSafePay(po.getOpenCollectionSafePay());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启关闭开具发票")
    @PostMapping("/update-open-invoice")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-invoice')")
    public ResultData<UserProfile> updateOpenInvoice(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenInvoice() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenInvoice(po.getOpenInvoice());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启关闭安全发到卡")
    @PostMapping("/update-open-safe-pay-bank")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-safe-pay-bank')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "开启关闭安全发到卡", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateOpenSafePayBank(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenSafePayBank() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenSafePayBank(po.getOpenSafePayBank());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    @ApiOperation("开启安全发签约授权新方式")
    @PostMapping("/update-open-safe-pay-new-sign-method")
    @ResponseBody
    @PreAuthorize("hasAnyRole('ADMIN', 'user::update-open-safe-pay-new-sign-method')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "开启安全发签约授权新方式", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<UserProfile> updateOpenSafePayNewSignMethod(@RequestBody UpdateUserPo po) {
        Preconditions.checkArgument(po.getId() != null && po.getOpenSafePayNewSignMethod() != null);
        UserProfile userProfile = userProfileManager.getById(po.getId());
        userProfile.setOpenSafePayNewSignMethod(po.getOpenSafePayNewSignMethod());
        userProfileManager.update(userProfile);
        return new ResultData<>(userProfile);
    }

    /**
     * 一键登录
     */
    @GetMapping("/login")
    @PreAuthorize("hasAnyRole('ADMIN')")
    @OperateLog(methodType = MethodType.UPDATE, methodDesc = "登录商户后台", module = FunctionModule.MERCHANT_MANAGE)
    public ResultData<String> loginUser(HttpServletResponse response, HttpServletRequest request, Long userId) {
        Preconditions.checkArgument(userId != null);

        String cookieDomain = domainHelper.getCookieDomain(request);
        cookieDomain = cookieDomain.substring(cookieDomain.indexOf(".") + 1);

        //把之前的session先给删除了
        CookieHelper.deleteCookie(response, cookieDomain, "/", WebConstant.SESSION_COOKIE_NAME);

        String key = sessionManager.getRememberMeKey(userId, ClientFrom.PC_WEB);
        Cookie rememberCookie = CookieHelper.createCookie("/", WebConstant.REMEMBER_ME_COOKIE_NAME, key, WebConstant.REMEMBER_ME_COOKIE_MAX_AGE, null);
        CookieHelper.setCookie(response, cookieDomain, rememberCookie);
        String merchantDomain = commonSysConfigHelper.getString(CommonSysConfigKey.MERCHANT_DOMAIN);
        return new ResultData<>(merchantDomain);
    }


    /**
     * 商户导出
     */
    @ApiOperation("商户导出")
    @GetMapping("/export")
    public ResultData<Boolean> exportUser(@ApiIgnore HttpServletRequest request, @ApiIgnore HttpServletResponse response) {

        UserSearchModel searchModel = new UserSearchModel();
        searchModel.setPageSize(10000L);
        searchModel.setSecurityStatus(SecurityStatus.NORMAL);
        searchModel.setMaxRegisterTime(LocalDateUtil.getYearStartDay(LocalDate.now()).atStartOfDay());
        List<User> users = userManager.search(searchModel);
        List<UserExportVo> vos = new ArrayList<>();

        for (User user : users) {
            UserExportVo vo = new UserExportVo();
            vo.setId(user.getId());
            vo.setRegisterTime(LocalDateUtil.getDateTimeAsString(user.getRegisterTime()));
            vo.setName(user.getName());
            vo.setMobile(user.getMobile());
            vo.setPartnerId(user.getPartnerId());
            if (user.getPartnerId() != null) {
                Partner partner = partnerManager.getById(user.getPartnerId());
                vo.setPartnerName(partner.getName());
            }

            UserShard userShard = userShardManager.getById(user.getId());
            try {
                ShardUtil.setShardContext(userShard.getShardId());
                UserAccount userAccount = userAccountManager.getById(user.getId());

                vo.setMoneyLeft(MoneyUtil.convertCentToString(userAccount.getMoneyLeft(), 2));
            } finally {
                ShardUtil.cleanShardContext();
            }

            vos.add(vo);
        }
        // 定义一个Map，往里面放入要在模板中显示数据
        Map<String, Object> model = new HashMap<>();

        model.put("records", vos);

        String fileName = "商户.xlsx";
        String template = "/excel/tpl/商户.xls";

        InputStream is;
        try {
            //获取模板流
            is = getClass().getResourceAsStream(template);

            //火狐游览器中文文件名会有乱码,兼容火狐
            String agent = request.getHeader("USER-AGENT").toLowerCase();

            if (!Strings.isNullOrEmpty(agent) && (agent.contains("firefox") || agent.contains("safari"))) {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            } else {
                fileName = URLEncoder.encode(fileName, "UTF-8");
            }
            response.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            JxlsUtils.exportExcel(is, response.getOutputStream(), model);
            response.flushBuffer();
        } catch (IOException e) {
            log.warn("export error", e);
        }
        return new ResultData<>();
    }
}
